Skip to content

feat(mcp): report the release-condition change from update-feature-flag - #114208

Open
jakesciotto wants to merge 1 commit into
posthog/mcp-update-feature-flag-add-users-descriptionfrom
posthog/mcp-update-feature-flag-filters-change
Open

jakesciotto wants to merge 1 commit into
posthog/mcp-update-feature-flag-add-users-descriptionfrom
posthog/mcp-update-feature-flag-filters-change

Conversation

@jakesciotto

Copy link
Copy Markdown
Contributor

Problem

An agent asked through MCP to add people to a feature flag can read the request as "restrict the flag to these people", add an is_not property filter to an existing release condition, and drop users who had the flag. The update-feature-flag result today is only the updated flag, so the agent gets no signal that its write removed access before it reports success.

This is the second layer of a stack. The layer below, #114186, tells the agent in the tool description which edits add users and which edits remove them.

Changes

  • When filters is sent, the tool result gains filters_change: changed, narrows, conditions_added, conditions_removed, conditions_changed (added, removed and changed property filters, plus the rollout before and after) and a summary in plain words with conditions numbered from 1 as in the UI. Example summary: "Condition 1 gained the filter organization_id is_not [...], so it now serves fewer users."
  • narrows is true when a changed condition gained a property filter, an exact filter lost values, an is_not filter gained values, a condition was removed, or a rollout percentage went down.
  • The description ends with a new paragraph: "When you send filters, the response includes filters_change. Read its summary. When filters_change.narrows is true, tell the user which condition now serves fewer users, and confirm that they asked to restrict access."
  • The request hook keeps the existing flag's filters from the GET it already makes, and the response hook compares them with the filters in the PATCH response. No API request is added and the PATCH body is unchanged.
  • Mechanical: services/mcp/schema/generated-tool-definitions.json and services/mcp/schema/tool-definitions-all.json regenerated; only the description string changed.

Note

Conditions are matched by property set, variant and group aggregation, so reordering conditions, or the values inside a filter, reports no change. Conditions left unmatched pair up in order and report as changed, so a rewritten condition reads as one change rather than a removal plus an addition.

How did you test this code?

Test rationale: The regression is the tool result omitting or misreporting the effect of a filters write. The closest existing test, update-feature-flag-preserving-groups.test.ts, checks the request side (the merge and the PATCH body) and asserts nothing about the result, so the new cases live in a sibling unit test that drives the same generated handler with a mocked API.

New unit tests in services/mcp/tests/unit/update-feature-flag-filters-change.test.ts:

  1. An is_not filter added to an existing condition: narrows is true and the filter is in properties_added.
  2. Two values added to an existing exact filter: narrows is false and values_added lists both.
  3. A condition inserted at index 0 with the old ones kept: conditions_added is [0], conditions_changed is empty, narrows is false.
  4. The same conditions in a different order: changed is false.
  5. No filters param: no filters_change key and one API request, the PATCH.

Commands run locally:

  • pnpm --filter=@posthog/mcp exec vitest run tests/unit
  • pnpm --filter=@posthog/mcp run typecheck
  • pnpm --filter=@posthog/mcp run lint
  • pnpm --filter=@posthog/mcp run format:check reports three files this PR does not touch (src/tools/notebooks/runNotebook.ts, src/tools/notebooks/runNotebookStatus.ts, src/tools/render-ui.ts); the files in this PR pass.
  • pnpm --filter=@posthog/mcp run generate-tools and pnpm --filter=@posthog/mcp run lint-tool-names

No live MCP session ran against a PostHog instance, and no agent eval ran, so the change in agent behavior is not measured.

Release status

  • No feature flag controls this change

Docs update

None. The tool description is the documentation surface for this tool.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Fable 5.1

Written from a work order in a PostHog Desktop task. The motivating case was a customer report of an agent narrowing a flag; nothing from that report is in this PR, and the test data is invented. Stacked on #114186 because both PRs edit the same description and the same generated files. Repo skills read before writing: writing-pr-descriptions, writing-tests, stacking-prs, implementing-mcp-tools. The summary wording was checked by hand against extra scenarios (rollout changes, removed conditions, scalar value changes) beyond the five committed cases.


Created with PostHog Desktop

🤖 Generated with Claude Code

@jakesciotto jakesciotto self-assigned this Oct 8, 2026
@jakesciotto
jakesciotto added this pull request to stack #114209 October 8, 2026 22:01
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Duplication (Python) — 26 new duplicated blocks (worst 628 tokens)

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
ee/hogai/chat_agent/sql/prompts.py:62 posthog/hogql/ai.py:65 67 628
products/batch_exports/backend/tests/destination_tests/test_bigquery_destination_tests.py:39 products/batch_exports/backend/tests/temporal/destinations/bigquery/conftest.py:24 49 224
products/warehouse_sources/backend/temporal/data_imports/sources/fulcrum/tests/test_fulcrum.py:51 products/warehouse_sources/backend/temporal/data_imports/sources/lemlist/tests/test_lemlist.py:43 23 205
products/messaging/backend/tests/api/test_message_preferences.py:33 products/messaging/backend/tests/test_message_preferences.py:15 26 193
products/warehouse_sources/backend/temporal/data_imports/sources/devin_ai/devin_ai.py:6 products/warehouse_sources/backend/temporal/data_imports/sources/lightspeed_retail/lightspeed_retail.py:6 23 177
products/warehouse_sources/backend/temporal/data_imports/sources/gusto/source.py:1 products/warehouse_sources/backend/temporal/data_imports/sources/shopify/source.py:1 27 154
products/batch_exports/backend/destination_tests/snowflake.py:161 products/batch_exports/backend/destination_tests/snowflake.py:345 27 127
products/warehouse_sources/backend/temporal/data_imports/sources/drip/drip.py:5 products/warehouse_sources/backend/temporal/data_imports/sources/lever/lever.py:5 13 127
products/batch_exports/backend/destination_tests/databricks.py:192 products/batch_exports/backend/destination_tests/databricks.py:258 24 123
products/warehouse_sources/backend/temporal/data_imports/sources/dovetail/dovetail.py:3 products/warehouse_sources/backend/temporal/data_imports/sources/linode/linode.py:4 18 122
products/ai_observability/backend/models/llm_prompt.py:158 products/skills/backend/models/skills.py:223 14 105
products/batch_exports/backend/destination_tests/snowflake.py:161 products/batch_exports/backend/destination_tests/snowflake.py:250 21 104
products/warehouse_sources/backend/temporal/data_imports/sources/emailoctopus/emailoctopus.py:13 products/warehouse_sources/backend/temporal/data_imports/sources/lemlist/lemlist.py:16 12 98
products/business_knowledge/backend/temporal/schedule.py:35 products/signals/backend/emission/conversations_schedule.py:26 18 97
products/warehouse_sources/backend/temporal/data_imports/sources/ding_connect/ding_connect.py:2 products/warehouse_sources/backend/temporal/data_imports/sources/less_annoying_crm/less_annoying_crm.py:3 16 97
products/batch_exports/backend/destination_tests/snowflake.py:260 products/batch_exports/backend/destination_tests/snowflake.py:355 18 92
products/warehouse_sources/backend/temporal/data_imports/sources/google_ads/schemas.py:256 products/warehouse_sources/backend/temporal/data_imports/sources/google_ads/schemas.py:587 46 92
products/warehouse_sources/backend/temporal/data_imports/sources/bitrise/bitrise.py:10 products/warehouse_sources/backend/temporal/data_imports/sources/launchdarkly/launchdarkly.py:7 11 88
products/warehouse_sources/backend/temporal/data_imports/sources/coingecko/coingecko.py:581 products/warehouse_sources/backend/temporal/data_imports/sources/less_annoying_crm/less_annoying_crm.py:291 11 82
products/warehouse_sources/backend/temporal/data_imports/sources/flowlu/flowlu.py:73 products/warehouse_sources/backend/temporal/data_imports/sources/linode/linode.py:130 15 82
products/messaging/backend/presentation/views/message_preferences.py:5 products/messaging/backend/presentation/views/message_suppression.py:3 12 75
products/warehouse_sources/backend/temporal/data_imports/sources/fillout/fillout.py:16 products/warehouse_sources/backend/temporal/data_imports/sources/lemlist/lemlist.py:12 11 74
products/metrics/backend/hogql_queries/metrics_histogram_query_runner.py:58 products/metrics/backend/hogql_queries/metrics_query_runner.py:83 11 73
products/warehouse_sources/backend/temporal/data_imports/sources/lemlist/lemlist.py:136 products/warehouse_sources/backend/temporal/data_imports/sources/semgrep/semgrep.py:280 16 72
products/warehouse_sources/backend/temporal/data_imports/sources/lemlist/lemlist.py:135 products/warehouse_sources/backend/temporal/data_imports/sources/lemlist/lemlist.py:223 17 71
posthog/api/services/llm_prompt.py:423 posthog/api/services/llm_prompt.py:499 20 70
⚠️ Duplication (TypeScript) — 16 new duplicated blocks (worst 252 tokens)

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
frontend/src/products.tsx:1512 products/engineering_analytics/manifest.tsx:112 24 252
nodejs/src/ingestion/pipelines/metrics/services/metrics-rate-limiter.service.ts:177 nodejs/src/logs/services/logs-rate-limiter.service.ts:178 28 193
nodejs/src/servers/ingestion-api-server.ts:223 nodejs/src/servers/ingestion-general-server.ts:214 32 151
nodejs/src/ingestion/pipelines/metrics/services/metrics-rate-limiter.service.ts:225 nodejs/src/logs/services/logs-rate-limiter.service.ts:231 25 124
nodejs/src/ingestion/pipelines/metrics/services/metrics-rate-limiter.service.ts:205 nodejs/src/logs/services/logs-rate-limiter.service.ts:209 20 118
frontend/src/scenes/feature-flags/featureFlagNotebookWidgetViews.tsx:109 frontend/src/scenes/feature-flags/featureFlagNotebookWidgetViews.tsx:192 21 113
nodejs/src/ingestion/ingestion-consumer.ts:238 nodejs/src/servers/ingestion-api-server.ts:343 15 109
frontend/src/products.tsx:180 products/engineering_analytics/manifest.tsx:76 25 108
frontend/src/scenes/feature-flags/featureFlagNotebookWidgetViews.tsx:109 frontend/src/scenes/feature-flags/featureFlagNotebookWidgetViews.tsx:159 21 107
frontend/src/scenes/health-alerts/healthAlertsWizardConfig.ts:26 products/data_warehouse/frontend/shared/sourceAlerts/sourceAlertWizardConfig.ts:35 30 99
nodejs/src/servers/ingestion-logs-server.ts:174 nodejs/src/servers/ingestion-metrics-server.ts:116 14 83
products/ai_observability/frontend/prompts/llmPromptsLogic.ts:236 products/skills/frontend/llmSkillsLogic.ts:613 12 76
nodejs/src/ingestion/pipelines/metrics/services/metrics-rate-limiter.service.ts:153 nodejs/src/logs/services/logs-rate-limiter.service.ts:155 25 75
products/logs/frontend/components/LogsViewer/data/logsViewerDataLogic.ts:614 products/tracing/frontend/tracingDataLogic.ts:809 11 73
products/tracing/frontend/tracingDataLogic.ts:900 products/tracing/frontend/tracingDataLogic.ts:937 11 73
products/engineering_analytics/frontend/scenes/ciExplorerLogic.ts:442 products/engineering_analytics/frontend/scenes/pullRequestDetailLogic.ts:433 13 72
✅ Bundle size — 🟢 -1.25 MiB (-1.6%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 75.52 MiB · 🟢 -1.25 MiB (-1.6%)

File Size Δ vs base
render-query/src/render-query/render-query.js 24.16 MiB 🟢 -1.51 MiB (-5.9%)
exporter/src/exporter/scenes/ExporterNotebookScene.js 3.82 MiB 🔺 +82.0 KiB (+2.1%)
posthog-app/_parent/products/engineering_analytics/frontend/scenes/CIExplorerScene.js 66.4 KiB 🔺 +66.4 KiB (new)
posthog-app/_parent/products/review_hog/frontend/CodeReviewScene.js 114.0 KiB 🔺 +30.8 KiB (+37.0%)
posthog-app/_parent/products/metrics/frontend/MetricsScene.js 54.1 KiB 🔺 +29.1 KiB (+116.4%)
posthog-app/_parent/products/autoresearch/frontend/AutoresearchPipelineScene.js 88.4 KiB 🔺 +22.6 KiB (+34.4%)
posthog-app/_parent/products/mcp_analytics/frontend/MCPAnalyticsScene.js 202.9 KiB 🔺 +14.7 KiB (+7.8%)
posthog-app/_parent/products/engineering_analytics/frontend/scenes/PullRequestDetailScene.js 25.5 KiB 🟢 -6.3 KiB (-19.9%)
posthog-app/src/scenes/marketing-analytics/MarketingAnalyticsScene.js 164.1 KiB 🔺 +5.7 KiB (+3.6%)
exporter/src/queries/schema.js 1.26 MiB 🔺 +5.3 KiB (+0.4%)
posthog-app/src/queries/schema.js 1.26 MiB 🔺 +5.3 KiB (+0.4%)
posthog-app/_parent/products/workflows/frontend/Broadcasts/BroadcastScene.js 92.8 KiB 🔺 +5.3 KiB (+6.0%)
posthog-app/src/scenes/feature-flags/FeatureFlag.js 136.4 KiB 🔺 +3.6 KiB (+2.7%)
posthog-app/src/lib/monaco/CodeEditorImpl.js 31.6 KiB 🔺 +2.3 KiB (+7.9%)
exporter/src/lib/monaco/CodeEditorImpl.js 31.2 KiB 🔺 +2.3 KiB (+8.0%)
posthog-app/_parent/products/conversations/frontend/scenes/ticket/SupportTicketScene.js 73.3 KiB 🔺 +2.1 KiB (+2.9%)
posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScannersScene.js 84.4 KiB 🟢 -1.8 KiB (-2.1%)
toolbar/src/toolbar/debug/chunk-EventDebugMenu.js 302.8 KiB 🔺 +1.2 KiB (+0.4%)
posthog-app/src/scenes/cohorts/Cohort.js 44.4 KiB 🟢 -1.2 KiB (-2.6%)
posthog-app/_parent/products/engineering_analytics/frontend/scenes/EngineeringAnalyticsScene.js 79.8 KiB 🔺 +1.2 KiB (+1.5%)
posthog-app/_parent/products/data_warehouse/frontend/scenes/SourcesScene/SourcesScene.js 16.5 KiB 🔺 +1.1 KiB (+7.2%)
posthog-app/_parent/products/tracing/frontend/TracingScene.js 189.9 KiB 🔺 +1.1 KiB (+0.6%)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

✅ Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.67 MiB · 23 files 🔺 +14.4 KiB (+0.9%) █████████░ 90.6% of 1.84 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
3.82 MiB · 675 files 🔺 +17.8 KiB (+0.5%) █████████░ 94.7% of 4.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.78 MiB · 2,463 files 🔺 +34.2 KiB (+0.4%) █████████░ 93.2% of 8.34 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.92 MiB · 3,517 files 🟢 -57.4 KiB (-0.6%) █████████░ 90.8% of 10.92 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.79 MiB · 2,473 files 🔺 +34.3 KiB (+0.4%) █████████░ 90.8% of 8.58 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.52 MiB · 3,361 files 🟢 -71.6 KiB (-0.7%) █████████░ 90.6% of 10.51 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
12.33 MiB · 4,192 files 🔺 +33.7 KiB (+0.3%) ████████░░ 78.4% of 15.72 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/index.tsx
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
839 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
99.6 KiB src/lib/api.ts
93.6 KiB src/products.tsx
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
40.7 KiB src/lib/utils/eventUsageLogic.ts
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
29.0 KiB ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/schemas.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.1 KiB ../packages/quill/packages/quill/dist/index.js
99.6 KiB src/lib/api.ts
93.6 KiB src/products.tsx
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.1 KiB ../packages/quill/packages/quill/dist/index.js
99.6 KiB src/lib/api.ts
93.6 KiB src/products.tsx
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.1 KiB ../packages/quill/packages/quill/dist/index.js
99.6 KiB src/lib/api.ts
93.6 KiB src/products.tsx
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.1 KiB ../packages/quill/packages/quill/dist/index.js
99.6 KiB src/lib/api.ts
93.6 KiB src/products.tsx
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.0 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
112.1 KiB ../packages/quill/packages/quill/dist/index.js
99.6 KiB src/lib/api.ts
93.6 KiB src/products.tsx

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

✅ Toolbar bundle — eager 2.24 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.24 MiB · 19 files 🔺 +5.3 KiB (+0.2%) ████░░░░░░ 39.2% of 5.72 MiB
Deferred (lazy) 2.19 MiB · 44 files 🔺 +1.3 KiB (+0.1%) n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
865.2 KiB dist/toolbar/toolbar-app-46YQ5DFZ.css
671.0 KiB dist/toolbar/chunk-chunk-2OSLOBKH.js
259.5 KiB dist/toolbar/chunk-chunk-DS74BCMD.js
138.2 KiB dist/toolbar/chunk-chunk-3CWTGQZU.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-BDZBYVA7.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-VD4HB63I.js
21.7 KiB dist/toolbar/chunk-chunk-XFPYVCN6.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

✅ Dist folder size — 🟢 -3.50 MiB (-0.3%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1006.73 MiB · 🟢 -3.50 MiB (-0.3%)

ℹ️ MCP UI apps size — 32 app(s), 17214.8 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 597.9 KB 203.4 KB
action 454.2 KB 203.4 KB
action-list 564.3 KB 203.4 KB
cohort 453.2 KB 203.4 KB
cohort-list 563.3 KB 203.4 KB
email-template 453.0 KB 203.4 KB
error-details 469.7 KB 203.4 KB
error-issue 454.6 KB 203.4 KB
error-issue-list 564.9 KB 203.4 KB
experiment 561.4 KB 203.4 KB
experiment-list 565.0 KB 203.4 KB
experiment-results 566.4 KB 203.4 KB
feature-flag 566.9 KB 203.4 KB
feature-flag-list 570.6 KB 203.4 KB
feature-flag-testing 457.4 KB 203.4 KB
inline-scan 453.7 KB 203.4 KB
insight-actors 562.4 KB 203.4 KB
llm-costs 559.4 KB 203.4 KB
session-recording 455.4 KB 203.4 KB
survey 454.8 KB 203.4 KB
survey-global-stats 562.0 KB 203.4 KB
survey-list 565.0 KB 203.4 KB
survey-stats 562.0 KB 203.4 KB
trace-span 453.6 KB 203.4 KB
trace-span-list 564.2 KB 203.4 KB
vision-observation-list 563.4 KB 203.4 KB
workflow 453.5 KB 203.4 KB
workflow-list 563.6 KB 203.4 KB
loops-review 457.9 KB 203.4 KB
query-results 789.9 KB 203.4 KB
render-ui 873.5 KB 203.4 KB
visual-review-snapshots 458.0 KB 203.4 KB
ℹ️ MCP agent API — agent-facing tool changes

What this PR changes for agents, from the tool schema snapshots and tool definitions.

Tools changed (1):

Tool Params Scopes Annotations Schema chars
update-feature-flag description changed

@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

[Medium impact] Adds change reporting to feature flag updates.

Fix the missed scalar restriction warning and satisfy the test-mock requirement before merging.

Reviews (1) · Last reviewed commit: "feat(mcp): report the release-condition ..." · Reviewed by Greptile

Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts Outdated
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts Outdated
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts Outdated
Comment thread services/mcp/tests/unit/update-feature-flag-filters-change.test.ts
@jakesciotto
jakesciotto marked this pull request as ready for review October 8, 2026 22:11
@parameterai

parameterai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Risk: Medium · 1 medium

This delta adds early-exit awareness to the filters_change report: added conditions with sub-100% rollout under early_exit now set narrows and get a "may stop later conditions" sentence, which fixes the previously reported gap. One gap remains in the same logic: a change to early_exit itself (agent-set, or forced to false by the schema's zod default) is never compared, so a flag write that flips evaluation semantics and cuts users off is reported as "Release conditions did not change."

Sentinel reviewed bc5b777 · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 8, 2026 22:11
@pr-assigner-resolver-posthog

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:

  • @PostHog/team-feature-flags (products/feature_flags/product.yaml)

Soft owners come from each directory's owners.yaml and each product's product.yaml (resolved nearest-file-wins). For a skipped owner, the locator is the file that decided it. Generated files and lockfiles are ignored when deciding ownership.

Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts
@trunk-io

trunk-io Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@patricio-posthog patricio-posthog left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good direction. A report back to the agent after the write is a stronger guard than description text only. I checked that _previousFilters does not go into the PATCH body, because the generated handler builds the body from named fields only.

The main concern: the description tells the agent to trust narrows and summary, but some edits that remove access give narrows: false or a summary that hides the narrowing. When the code cannot classify a change, it should not report "no narrowing". I left inline comments on the cases.

Smaller points, not blocking:

  • An edit that both narrows and widens one condition gets "so it now serves fewer users". For example: add a filter and move the rollout from 0% to 100%. Keep narrows conservative, but the summary can say "changed who it serves" for mixed edits.
  • The test mocks cast to any. Typed mocks catch breakage when Context changes.
  • The cost of matchInOrder grows with the square of the condition count. I think the risk is low for real flags, but a cap on the number of conditions to compare is cheap.

This PR is stacked on #114186, which still has open comments, so that PR must land first.

Please add a unit test for each inline case.

Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts Outdated
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts Outdated
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts
@haacked haacked added team/feature-flags feature/feature-flags Feature Tag: Feature flags labels Oct 9, 2026
@jakesciotto
jakesciotto force-pushed the posthog/mcp-update-feature-flag-filters-change branch from 6e4b82c to 706882c Compare October 9, 2026 21:29
Comment thread services/mcp/src/tools/featureFlags/describeFiltersChange.ts
Report sub-100% release conditions added to early-exit flags as potentially restricting access.
@jakesciotto
jakesciotto force-pushed the posthog/mcp-update-feature-flag-filters-change branch from 706882c to bc5b777 Compare October 9, 2026 21:44
changed: conditionsAdded.length > 0 || conditionsRemoved.length > 0 || conditionsChanged.length > 0,
narrows:
conditionsRemoved.length > 0 ||
(earlyExit && addedConditions.some((condition) => condition.rollout < 100)) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Turning on early_exit removes users' access but the tool result says nothing changed

An agent can narrow a flag by flipping filters.early_exit to true (or have it silently forced to false), and filters_change reports "Release conditions did not change" — the exact silent-access-loss this PR exists to prevent. The new early-exit clause only inspects condition groups; the early_exit value itself is never compared.

How:

  1. A flag has early_exit: false, condition 1 at 50% rollout, condition 2 serving other users; users matching condition 1 but failing its rollout get the flag via condition 2.
  2. The agent calls update-feature-flag with filters: { groups: <unchanged>, early_exit: true } (the param is accepted at services/mcp/src/generated/feature_flags/api.ts:825 and spread through the merge at preserveGroupTargeting.ts:244).
  3. Evaluation now stops at condition 1's OutOfRolloutBound (rust/feature-flags/src/flags/flag_matching.rs:1947-1970), so those users lose the flag.
  4. describeFiltersChange matches all groups as unchanged (lines 421-433), so changed: false, narrows: false and the summary says nothing changed. Note also the zod default false (api.ts:526): any filters update that omits early_exit forces it to false, silently disabling early exit — likewise reported as no change.

Fix: Compare previous.early_exit and next.early_exit in describeFiltersChange (treat a flip as a change; enabling it while any earlier condition has <100% rollout sets narrows), and restore an omitted early_exit from the existing flag in preserveGroupTargetingFilters so the zod default cannot clobber it.


React with 👍 if useful or 👎 if not

if (!unmatchedAfter.has(next)) {
continue
}
const previous = before.find((candidate) => unmatchedBefore.has(candidate) && isSame(candidate, next))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Quadratic property matching enables denial of service

A caller with write access to their own flag can alternate one condition containing 10,000 person is_set properties keyed p0–p9999 and q0–q9999: each filters object is about 507 KiB, within the backend's 512 KiB limit, but diffProperties performs this full scan twice for every property. A local reproduction blocked an event-loop timer for approximately 15 seconds; this runs synchronously in the shared Hono/Node process, so unrelated MCP requests stall too, and the existing request-rate limits permit repeating the attack.

Use signature/identity-indexed matching, or bound total property-matching work and return the conservative fallback when that budget is exceeded—the 500-condition guard does not bound properties within a condition.

@veria-ai

veria-ai Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

PR overview

The PR adds release-condition change reporting to the MCP update-feature-flag tool, including comparisons of properties in feature flag filters.

An unresolved quadratic property comparison allows a caller with write access to their own flag to submit large, valid conditions that block the shared MCP process. A local reproduction stalled the event loop for approximately 15 seconds, delaying unrelated requests, and existing rate limits allow the operation to be repeated. No issues have been addressed yet.

Open issues (1)

Fixed/addressed: 0 · PR risk: 7/10

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants